package com.b511.swpu.cementbackend.service.impl;

import com.b511.swpu.cementbackend.dto.output.BoreholeErrorInfoOutputDto;
import com.b511.swpu.cementbackend.dto.output.ErrorBoreholeOutputDto;
import com.b511.swpu.cementbackend.dto.output.ValidateInfoOutputDto;
import com.b511.swpu.cementbackend.entity.*;
import com.b511.swpu.cementbackend.exception.CementException;
import com.b511.swpu.cementbackend.repository.*;
import com.b511.swpu.cementbackend.service.ValidateService;
import com.b511.swpu.cementbackend.util.R;
import com.b511.swpu.cementbackend.util.ValidateUtil;
import com.b511.swpu.cementbackend.util.enumeration.Code;
import com.b511.swpu.cementbackend.util.enumeration.Table;
import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;

@Service
public class ValidateServiceImpl implements ValidateService {

    private Map<CementValidateRepository, Class> map;

    private final ValidateUtil validateUtil;

    private final BoreholeRepository boreholeRepository;

    private final WellBoreStructureRepository wellBoreStructureRepository;

    private final StratumRepository stratumRepository;

    private final PorePressureRepository porePressureRepository;

    private final BurstPressureRepository burstPressureRepository;


    public ValidateServiceImpl(ValidateUtil validateUtil, BoreholeRepository boreholeRepository, WellBoreStructureRepository wellBoreStructureRepository, StratumRepository stratumRepository, PorePressureRepository porePressureRepository, BurstPressureRepository burstPressureRepository) {
        this.validateUtil = validateUtil;
        this.boreholeRepository = boreholeRepository;
        this.wellBoreStructureRepository = wellBoreStructureRepository;
        this.stratumRepository = stratumRepository;
        this.porePressureRepository = porePressureRepository;
        this.burstPressureRepository = burstPressureRepository;
        initMap();
    }

    private void initMap() {
        map = new HashMap<>();
        map.put(boreholeRepository, Borehole.class);
        map.put(wellBoreStructureRepository, WellBoreStructure.class);
        map.put(stratumRepository, Stratum.class);
        map.put(porePressureRepository, PorePressure.class);
        map.put(burstPressureRepository, BurstPressure.class);
    }

    @Override
    @Transactional
    public R validate() {
        map.forEach(this::validateAndSave);
        return R.ok();
    }

    private <T extends CementBaseEntity> void validateAndSave(CementValidateRepository cementValidateRepository, Class clazz) {
        //未校验-未设置状态
        List<T> list = cementValidateRepository.findByIsCheckFalseOrIsCheckIsNull();
        validateUtil.setRules(clazz);
        list.forEach(validateUtil::validate);
        cementValidateRepository.saveAll(list);
    }


    @Override
    public R getErrorBorehole() {
        Set<Borehole> boreholes = new HashSet<>(boreholeRepository.findByIsErrorTrue());
        Set<BoreholeErrorInfoOutputDto> result = new HashSet<>();
        // WellBoreStructure
        List<WellBoreStructure> wellBoreStructures = wellBoreStructureRepository.findByIsErrorTrue();
        for(WellBoreStructure wellbore : wellBoreStructures){
            boreholes.add(wellbore.getBorehole());
        }
        // Stratum
        List<Stratum> stratumList = stratumRepository.findByIsErrorTrue();
        for(Stratum stratum:stratumList){
            boreholes.add(stratum.getBorehole());
        }
        // PorePressure
        List<PorePressure> porePressureList = porePressureRepository.findByIsErrorTrue();
        for(PorePressure porePressure : porePressureList){
            boreholes.add(porePressure.getBorehole());
        }
        // BurstPressure
        List<BurstPressure> burstPressureList = burstPressureRepository.findByIsErrorTrue();
        for(BurstPressure burstPressure : burstPressureList){
            boreholes.add(burstPressure.getBorehole());
        }

        // copyProperty
        for (Borehole borehole : boreholes) {
            BoreholeErrorInfoOutputDto outputDto = new BoreholeErrorInfoOutputDto();
            BeanUtils.copyProperties(borehole, outputDto);
            result.add(outputDto);
        }

        return R.ok().putData(result);
    }

    @Override
    public R getErrorBoreholeById(Long boreholeId) {
        Optional<Borehole> optional = boreholeRepository.findById(boreholeId);
        if (!optional.isPresent()) {
            throw new CementException(Code.SELECT_FAIL);
        }
        return R.ok().putData(new ArrayList<>(Arrays.asList(getErrorBoreholeOutputDtoArray(optional.get().getId()))));
    }

    private <T extends CementBaseEntity> List<CementBaseEntity>  getCementBaseEntity (List<T> inputList){
        List<CementBaseEntity> baseEntities = new ArrayList<>();
        for (T list:inputList) {
            CementBaseEntity baseEntity = new CementBaseEntity();
            BeanUtils.copyProperties(list, baseEntity);
            baseEntities.add(baseEntity);
        }
        return baseEntities;
    }

    private ErrorBoreholeOutputDto[] getErrorBoreholeOutputDtoArray(Long id) {
        List<WellBoreStructure> wellBoreStructures = wellBoreStructureRepository.findByBoreholeIdAndIsErrorTrue(id);
        List<Stratum> stratum = stratumRepository.findByBoreholeIdAndIsErrorTrue(id);
        List<PorePressure> porePressures = porePressureRepository.findByBoreholeIdAndIsErrorTrue(id);
        List<BurstPressure> burstPressures = burstPressureRepository.findByBoreholeIdAndIsErrorTrue(id);

        return new ErrorBoreholeOutputDto[]{
                new ErrorBoreholeOutputDto().setList(getCementBaseEntity(wellBoreStructures)).setLabel(Table.WELL_STRUCTURE.getName()),
                new ErrorBoreholeOutputDto().setList(getCementBaseEntity(stratum)).setLabel(Table.STRATUM.getName()),
                new ErrorBoreholeOutputDto().setList(getCementBaseEntity(porePressures)).setLabel(Table.PORE_PRESSURE.getName()),
                new ErrorBoreholeOutputDto().setList(getCementBaseEntity(burstPressures)).setLabel(Table.BURST_PRESSURE.getName()),
        };
    }
}
